package com.kexin.coupon.service.impl;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kexin.common.exception.WebException;
import com.kexin.common.user.entity.UserBase;
import com.kexin.common.user.service.IUserBaseService;
import com.kexin.common.utils.MybatisplusQueryUtils;
import com.kexin.common.utils.OrderUtil;
import com.kexin.coupon.api.CouponApi;
import com.kexin.coupon.entity.Coupon;
import com.kexin.coupon.entity.CouponOrder;
import com.kexin.coupon.mapper.CouponOrderMapper;
import com.kexin.coupon.service.ICouponOrderService;
import com.kexin.coupon.service.ICouponService;
import com.kexin.coupon.vo.OrderDetail;
import com.kexin.coupon.vo.PlaceOrder;
import com.kexin.fpay.api.FPayApi;

/**
 * 卡券订单表 服务实现类
 * @author ao
 */
@Service
public class CouponOrderServiceImpl extends ServiceImpl<CouponOrderMapper, CouponOrder> implements ICouponOrderService {

	Logger log = LoggerFactory.getLogger(CouponOrderServiceImpl.class);

	@Autowired
	private ICouponService couponService;

	@Autowired
	private IUserBaseService userBaseService;

	@Transactional(rollbackFor = Exception.class)
	@Override
	public OrderDetail submitOrder(Integer userId, Integer couponId, Integer num) {
		Coupon coupon = couponService.getOne(couponId);
		if (coupon==null) {
			throw new WebException("此卡券不存在");
		}
		if (num==null) {
			num = 1;
		}
		
		//更新卡券库存
		couponService.updateCount(couponId, userId, num, 1);
		
		String orderNo = OrderUtil.orderNo();
		LocalDateTime localDateTime = LocalDateTime.now();
		
		CouponOrder couponOrder = new CouponOrder();
		
		couponOrder.setOrderNo(orderNo);
		couponOrder.setUserId(userId);
		couponOrder.setChannelType(coupon.getChannelType());
		couponOrder.setCouponId(coupon.getId());
		couponOrder.setCouponCode(coupon.getCouponCode());
		couponOrder.setCouponName(coupon.getCouponName());
		couponOrder.setDetail(coupon.getDetail());
		couponOrder.setPic(coupon.getPic());
		couponOrder.setLinkUrl(coupon.getLinkUrl());
		couponOrder.setFaceValue(coupon.getFaceValue());
		couponOrder.setPrice(coupon.getPrice());
		couponOrder.setNum(num);
		couponOrder.setTotalPrice(coupon.getPrice()*num);
		couponOrder.setBuyChannel(coupon.getBuyChannel());
		couponOrder.setHasApplyNotify(coupon.getHasApplyNotify());
		couponOrder.setHasRefund(coupon.getHasRefund());
		couponOrder.setHasUsedNotify(coupon.getHasUsedNotify());
		couponOrder.setBrandName(coupon.getBrandName());
		couponOrder.setCreateTime(localDateTime);
		couponOrder.setUpdateTime(localDateTime);
		
		save(couponOrder);
		
		OrderDetail orderDetail = new OrderDetail();
		
		orderDetail.setOrderNo(couponOrder.getOrderNo());
		orderDetail.setCouponCode(couponOrder.getCouponCode());
		orderDetail.setCouponName(couponOrder.getCouponName());
		orderDetail.setNum(couponOrder.getNum());
		orderDetail.setTotalPrice(couponOrder.getTotalPrice());
		
		return orderDetail;
	}

	@Override
	public void cancelOrder(String orderNo) {
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("order_no", orderNo);
		CouponOrder couponOrder = getOne(query);
		if (couponOrder==null) {
			throw new WebException("卡券订单不存在");
		}
		String payState = couponOrder.getPayState();
		if (!"0".equals(payState)) {
			throw new WebException("此卡券订单不是未支付状态");
		}
		couponOrder.setPayState("2");
		//couponOrder.setUseState("2");
		updateById(couponOrder);
		//更新卡券库存
		couponService.updateCount(couponOrder.getCouponId(), couponOrder.getUserId(), couponOrder.getNum(), 2);
	}

	@Override
	public int getUserBuyedCount(Integer userId, String couponCode, Object ... payState) {
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("user_id", userId);
		query.eq("coupon_code", couponCode);
		query.in("pay_state", payState);
		query.select("sum(num) as buy_count");
		Map<String, Object> resultMap = getMap(query);
		int buyCount = 0;
		if (resultMap!=null && resultMap.get("buy_count")!=null) {
			buyCount = Integer.parseInt(String.valueOf(resultMap.get("buy_count")));
		}
		return buyCount;
	}

	@SuppressWarnings("unchecked")
	@Override
	public PlaceOrder placeAnOrder(String orderNo) {
		if (StringUtils.isBlank(orderNo)) {
			throw new WebException("订单号为必输项");
		}
		synchronized (orderNo.intern()) {
			QueryWrapper<CouponOrder> query = Wrappers.query();
			query.eq("order_no", orderNo);
			CouponOrder couponOrder = getOne(query);
			if (couponOrder==null) {
				throw new WebException("卡券订单不存在");
			}
			String tn = null;
			if (StringUtils.isEmpty(couponOrder.getTn())) {
				Map<String, Object> placeResult = FPayApi.placeAnOrder(orderNo, 
						couponOrder.getCouponName(), couponOrder.getTotalPrice());
				if (!"0".equals(placeResult.get("code"))) {
					throw new WebException((String)placeResult.get("msg"));
				}
				Map<String, Object> data = (Map<String, Object>) placeResult.get("data");
				JSONObject payInfo = JSONObject.parseObject((String) data.get("payInfo"));
				tn = payInfo.getString("tn");
				
				UpdateWrapper<CouponOrder> update = Wrappers.update();
				update.eq("id", couponOrder.getId());
				update.set("tn", tn);
				update.set("update_time", new Date());
				update(update);
			} else {
				tn = couponOrder.getTn();
			}
			
			PlaceOrder placeOrder = new PlaceOrder();
			placeOrder.setOrderNo(orderNo);
			placeOrder.setTn(tn);
			
			return placeOrder;
		}
	}

	@Override
	public void submitRefund(String orderNo) {
		if (StringUtils.isBlank(orderNo)) {
			throw new WebException("订单号为必输项");
		}
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("order_no", orderNo);
		CouponOrder couponOrder = getOne(query);
		if (couponOrder==null) {
			throw new WebException("卡券订单不存在");
		}
		String payState = couponOrder.getPayState();
		if (!"1".equals(payState)) {
			throw new WebException("订单未支付或着已过期");
		}
		String hasRefund = couponOrder.getHasRefund();
		if ("1".equals(hasRefund)) {
			throw new WebException("该卡券不允许退款");
		}
		String useState = couponOrder.getUseState();
		if ("1".equals(useState)) {
			throw new WebException("卡券已使用");
		}
		String refundState = couponOrder.getRefundState();
		if ("1".equals(refundState)) {
			throw new WebException("订单已退款");
		} else if ("2".equals(refundState)) {
			throw new WebException("退款失败，请联系后台人员处理");
		} else if ("3".equals(refundState)) {
			throw new WebException("订单退款中");
		} else if ("4".equals(refundState)) {
			throw new WebException("退款审核中");
		}
		couponOrder.setRefundState("4");
		updateById(couponOrder);
	}

	@Override
	public void verifyRefund(String orderNo, String state) {
		if (StringUtils.isBlank(orderNo)) {
			throw new WebException("订单号为必输项");
		}
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("order_no", orderNo);
		CouponOrder couponOrder = getOne(query);
		if (couponOrder==null) {
			throw new WebException("卡券订单不存在");
		}
		String payState = couponOrder.getPayState();
		if (!"1".equals(payState)) {
			throw new WebException("订单未支付或着已过期");
		}
		
		String refundState = couponOrder.getRefundState();
		if ("1".equals(refundState)) {
			throw new WebException("订单已退款");
		} else if ("2".equals(refundState)) {
			throw new WebException("退款失败，请联系后台人员处理");
		}
		
		if (StringUtils.isBlank(state)) {
			throw new WebException("审核码为必输项");
		} else if ("1".equals(state)) {
			//同意退款
			refund(couponOrder);
		} else if ("2".equals(state)) {
			//拒绝退款
			couponOrder.setRefundState("0");
			updateById(couponOrder);
		} else {
			throw new WebException("审核状态有误[state="+state+"]");
		}
		
	}

	@SuppressWarnings("unchecked")
	private void refund(CouponOrder couponOrder) {
		if (couponOrder==null) {
			throw new WebException("卡券订单不存在");
		}
		String payState = couponOrder.getPayState();
		if (!"1".equals(payState)) {
			throw new WebException("订单未支付或着已过期");
		}
		String refundState = couponOrder.getRefundState();
		if ("1".equals(refundState)) {
			throw new WebException("订单已退款");
		} else if ("3".equals(refundState)) {
			throw new WebException("订单退款中");
		}
		String outRefundNo = OrderUtil.tpOrderNo();
		String refundDesc = "卡券订单退款";
		Integer amount = couponOrder.getAmount();
		
		Map<String, Object> refundResult = FPayApi.refund(
				couponOrder.getOrderNo(), refundDesc, outRefundNo, amount);
		if (!"0".equals(refundResult.get("code"))) {
			throw new WebException((String) refundResult.get("msg"));
		}
		
		LocalDateTime localDateTime = LocalDateTime.now();
		couponOrder.setRefundState("3");
		couponOrder.setRefundTime(localDateTime);
		couponOrder.setUpdateTime(localDateTime);
		couponOrder.setRefundOrderNo(outRefundNo);
		
		try {
			//实时查询退款状态
			Map<String, Object> queryResult = FPayApi
					.query("2", couponOrder.getRefundOrderNo());
			if ("0".equals(queryResult.get("code"))) {
				Map<String, Object> data0 = (Map<String, Object>) queryResult.get("data");
				String refundStatus = (String) data0.get("refundStatus");
				if ("1".equals(refundStatus)||"2".equals(refundStatus)) {
					couponOrder.setRefundState("1");
				}
			}
		} catch (Exception e) {
			log.info("实时查询退款状态出错");
			e.printStackTrace();
		}
		
		//更新卡券库存
		couponService.updateCount(couponOrder.getCouponId(), couponOrder.getUserId(), couponOrder.getNum(), 4);
		
		try {
			//调用卡券申领平台接口退货
			String busiCouponRefundOrderNo = OrderUtil.tpOrderNo();
			Map<String, Object> busiRefundResult = CouponApi.busiRefund(couponOrder.getChannelType(), 
					busiCouponRefundOrderNo, couponOrder.getBusiCouponOrderNo(), 
					couponOrder.getCouponCode(), couponOrder.getCouponNo());
			if ("0".equals(busiRefundResult.get("code"))) {
				Map<String, Object> data = (Map<String, Object>) busiRefundResult.get("data");
				String busiOrderNo = (String) data.get("orderNo");
				String busiRefundState = (String) data.get("refundState");
				List<Map<String, Object>> couponNos = (List<Map<String, Object>>) data.get("couponNos");
				Map<String, Object> couponObject = null;
				if (couponNos!=null) {
					couponObject = couponNos.get(0);
				}
				String couponNo = null;
				String state = null;
				if (couponObject!=null) {
					couponNo = (String) couponObject.get("couponNo");
					state = (String) couponObject.get("state");
				}
				log.info("busiOrderNo="+busiOrderNo+";busiRefundState="
						+busiRefundState+",couponNo="+couponNo+",state="+state);
				couponOrder.setBusiCouponRefundOrderNo(busiCouponRefundOrderNo);
			}
		} catch (Exception e) {
			log.info("卡券退货失败");
			e.printStackTrace();
			couponOrder.setRefundState("2");
		}
		updateById(couponOrder);
	}

	@Override
	public void refund(String orderNo) {
		if (StringUtils.isBlank(orderNo)) {
			throw new WebException("订单号为必输项");
		}
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("order_no", orderNo);
		refund(getOne(query));
	}

	@Override
	public Page<CouponOrder> queryList(Integer userId, String couponName, 
			String payState, String useState, String refundState, 
			Integer current, Integer size) {
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.like(StringUtils.isNotBlank(couponName), "coupon_name", couponName);
		query.eq("user_id", userId);
		query.in(StringUtils.isNotBlank(payState), "pay_state", 
				MybatisplusQueryUtils.getValueArray(payState, ",", 1));
		query.in(StringUtils.isNotBlank(useState), "use_state", 
				MybatisplusQueryUtils.getValueArray(useState, ",", 1));
		query.in(StringUtils.isNotBlank(refundState), "refund_state", 
				MybatisplusQueryUtils.getValueArray(refundState, ",", 1));
		query.orderByDesc("create_time");
		if (current==null) {
			current = 1;
		}
		if (size==null) {
			size = 10;
		}
		return page(new Page<CouponOrder>(current, size), query);
	}

	@SuppressWarnings("unchecked")
	@Transactional(rollbackFor = Exception.class)
	@Override
	public void result(Map<String, Object> param) {
		
		String outTransNo = (String) param.get("outTransNo");
		String payStatus = (String) param.get("payStatus");
		if (!"1".equals(payStatus)) {
			throw new WebException("支付失败[payStatus="+payStatus+"]");
		}
		
		int amount = Integer.parseInt((String) param.get("amount"));
		LocalDateTime payDateTime = null;
		try {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			Date payTime = sdf.parse((String) param.get("payTime"));
			Calendar calendar = Calendar.getInstance();
			int hour = calendar.get(Calendar.HOUR);
			int minute = calendar.get(Calendar.MINUTE);
			int second = calendar.get(Calendar.SECOND);
			int milliSecond = calendar.get(Calendar.MILLISECOND);
			calendar.setTime(payTime);
			calendar.set(Calendar.HOUR, hour);
			calendar.set(Calendar.MINUTE, minute);
			calendar.set(Calendar.SECOND, second);
			calendar.set(Calendar.MILLISECOND, milliSecond);
			payTime = calendar.getTime();
			payDateTime = payTime.toInstant().atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
		LocalDateTime nowLocalDateTime = LocalDateTime.now();
		
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("order_no", outTransNo);
		CouponOrder couponOrder = getOne(query);
		couponOrder.setPayState("1");
		couponOrder.setAmount(amount);
		couponOrder.setPayTime(payDateTime);
		couponOrder.setUpdateTime(nowLocalDateTime);
		
		try {
			UserBase user = userBaseService.getById(couponOrder.getUserId());
			String moblie = "1**********";
			if (user!=null) {
				moblie = user.getMobile();
			}
			String busiCouponOrderNo = OrderUtil.tpOrderNo();
			Map<String, Object> busiApplyResult = CouponApi.busiApply(
					couponOrder.getChannelType(), busiCouponOrderNo, 
					couponOrder.getNum(), couponOrder.getCouponCode(), "1", moblie);
			if (!"0".equals(busiApplyResult.get("code"))) {
				throw new WebException((String) busiApplyResult.get("message"));
			}
			Map<String, Object> data = (Map<String, Object>) busiApplyResult.get("data");
			List<Map<String, Object>> couponList = (List<Map<String, Object>>) data.get("couponList");
			Map<String, Object> couponMap = couponList.get(0);
			
			couponOrder.setCouponNo((String) couponMap.get("couponNo"));
			
			String validStartTime = (String) couponMap.get("validStartTime");
			String validEndTime = (String) couponMap.get("validEndTime");
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			couponOrder.setStartTime(sdf.parse(validStartTime).toInstant()
					.atZone(ZoneOffset.ofHours(8)).toLocalDateTime());
			couponOrder.setExpireTime(sdf.parse(validEndTime).toInstant()
					.atZone(ZoneOffset.ofHours(8)).toLocalDateTime());
			couponOrder.setBusiCouponOrderNo(busiCouponOrderNo);
			
		} catch (Exception e) {
			log.info("卡券申领失败："+e.getMessage());
			log.error("卡券申领失败："+e.getMessage(), e);
			e.printStackTrace();
		}
		updateById(couponOrder);
		
		//更新卡券库存
		couponService.updateCount(couponOrder.getCouponId(), couponOrder.getUserId(), couponOrder.getNum(), 3);
	}

	@Override
	public void used(JSONObject param) {
		String orderNo = param.getString("orderNo");
		String origOutOrderNo = param.getString("origOutOrderNo");
		
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("busi_coupon_order_no", origOutOrderNo);
		CouponOrder couponOrder = getOne(query);
		if (couponOrder==null) {
			return;
		}
		couponOrder.setPayState("3");
		couponOrder.setUseState("1");
		couponOrder.setBusiCouponUsedOrderNo(orderNo);
		updateById(couponOrder);
	}

	@Override
	public void cancelNotPayOrders() {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.MINUTE, -60);
		
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("pay_state", "0");
		query.lt("create_time", calendar.getTime());
		List<CouponOrder> couponOrders = list(query);
		if (couponOrders==null || couponOrders.size()==0) {
			return;
		}
		for (CouponOrder couponOrder : couponOrders) {
			try {
				cancelOrder(couponOrder.getOrderNo());
				log.info("订单"+couponOrder.getOrderNo()+"过期未支付已自动取消");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		log.info("总共"+couponOrders.size()+"个过期未支付的订单已自动取消");
	}

	@Override
	public void refundOutOfDateOrders() {
		Calendar calendar = Calendar.getInstance();
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("pay_state", "1");
		query.in("use_state", "0", "2");
		query.eq("refund_state", "0");
		query.eq("has_refund", "0");
		query.lt("expire_time", calendar.getTime());
		List<CouponOrder> couponOrders = list(query);
		if (couponOrders==null || couponOrders.size()==0) {
			return;
		}
		for (CouponOrder couponOrder : couponOrders) {
			try {
				couponOrder.setUseState("2");
				refund(couponOrder);
				log.info("订单"+couponOrder.getOrderNo()+"过期未使用已自动退款");
			} catch (Exception e) {
				e.printStackTrace();
				//标记为退款失败
				couponOrder.setRefundState("2");
				updateById(couponOrder);
				log.info("订单"+couponOrder.getOrderNo()+"过期未使用已自动退款失败，原因："+e.getMessage());
			}
		}
		log.info("总共"+couponOrders.size()+"个过期未使用的优惠券已退款");
	}

	@Override
	public void refundFailOrders() {
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("pay_state", "1");
		query.in("use_state", "0", "2");
		query.eq("refund_state", "2");
		query.eq("has_refund", "0");
		List<CouponOrder> couponOrders = list(query);
		if (couponOrders==null || couponOrders.size()==0) {
			return;
		}
		for (CouponOrder couponOrder : couponOrders) {
			try {
				refund(couponOrder.getOrderNo());
				log.info("订单"+couponOrder.getOrderNo()+"过期未使用已自动退款");
			} catch (Exception e) {
				e.printStackTrace();
				//标记为退款失败
				couponOrder.setRefundState("2");
				updateById(couponOrder);
				log.info("订单"+couponOrder.getOrderNo()+"过期未使用自动退款失败，原因："+e.getMessage());
			}
		}
		log.info("总共"+couponOrders.size()+"个退款失败的订单已重新退款");
	}

	@SuppressWarnings("unchecked")
	@Override
	public void updateRefundingOrders() {
		QueryWrapper<CouponOrder> query = Wrappers.query();
		query.eq("pay_state", "1");
		query.in("use_state", "0", "2");
		query.eq("refund_state", "3");
		query.eq("has_refund", "0");
		List<CouponOrder> couponOrders = list(query);
		if (couponOrders==null || couponOrders.size()==0) {
			return;
		}
		for (CouponOrder couponOrder : couponOrders) {
			try {
				
				Map<String, Object> queryResult = FPayApi
						.query("2", couponOrder.getRefundOrderNo());
				if (!"0".equals(queryResult.get("code"))) {
					throw new WebException((String)queryResult.get("code"));
				}
				Map<String, Object> data = (Map<String, Object>) queryResult.get("data");
				String refundStatus = (String) data.get("refundStatus");
				if ("0".equals(refundStatus)) {
					couponOrder.setRefundState("3");
				} else if ("1".equals(refundStatus)||"2".equals(refundStatus)) {
					couponOrder.setRefundState("1");
				} else if ("3".equals(refundStatus)) {
					couponOrder.setRefundState("2");
				}
				
				updateById(couponOrder);
				log.info("订单"+couponOrder.getOrderNo()+"退款成功");
			} catch (Exception e) {
				e.printStackTrace();
				log.info("退款失败的订单"+couponOrder.getOrderNo()+"再次退款失败，原因："+e.getMessage());
			}
		}
		log.info("总共"+couponOrders.size()+"个退款失败的订单已重新退款");
	}
}
